/**
 * Context类，提供通知和观察功能，用于增量渲染
 */
import { Scene } from './scene';
import { Entity } from './entity';

/**
 * 观察者回调类型
 */
type ObserverCallback = (entityId: string) => void;

/**
 * 上下文类，负责管理实体的通知和观察机制
 * 类似于Zed GPUI中的Context，用于触发增量渲染
 */
export class Context<T = any> {
  private entityId: string;
  private app: AppContext;
  private entity: T;

  constructor(entityId: string, app: AppContext, entity: T) {
    this.entityId = entityId;
    this.app = app;
    this.entity = entity;
  }

  /**
   * 获取当前实体
   */
  get value(): T {
    return this.entity;
  }

  /**
   * 设置当前实体
   */
  set value(newValue: T) {
    this.entity = newValue;
  }

  /**
   * 通知系统当前实体状态已更改，触发观察者通知和增量渲染
   * 这是增量渲染的关键方法，当组件状态变化时调用
   * 类似于Zed GPUI中的cx.notify()
   */
  notify(): void {
    this.app.notify(this.entityId);
  }

  /**
   * 观察另一个实体的变化
   * @param observedId 被观察实体的ID
   * @param callback 观察到变化时的回调函数
   * @returns 订阅对象，可以用于取消订阅
   */
  observe(observedId: string, callback: ObserverCallback): Subscription {
    return this.app.observe(observedId, this.entityId, callback);
  }

  /**
   * 取消观察另一个实体
   * @param subscription 订阅对象
   */
  unobserve(subscription: Subscription): void {
    this.app.unobserve(subscription);
  }

  /**
   * 获取当前实体的Entity对象
   * 用于在组件中进行状态更新
   */
  getEntity(): Entity<T> {
    return this.app.getEntity(this.entityId);
  }
}

/**
 * 订阅类，用于管理实体间的观察关系
 */
export class Subscription {
  private id: string;
  private appContext: AppContext;

  constructor(id: string, appContext: AppContext) {
    this.id = id;
    this.appContext = appContext;
  }

  /**
   * 获取订阅ID
   */
  getId(): string {
    return this.id;
  }

  /**
   * 取消订阅
   * 类似于Zed GPUI中的subscription.detach()
   */
  detach(): void {
    this.appContext.unobserve(this);
  }
}

/**
 * AppContext类，管理所有实体的通知和观察机制
 */
export class AppContext {
  private observers: Map<string, Map<string, ObserverCallback>> = new Map();
  private subscriptions: Map<string, { observedId: string; observerId: string }> = new Map();
  private pendingNotifications: Set<string> = new Set();
  private nextSubscriptionId: number = 0;
  private renderer: any = null; // Renderer的引用
  private scenes: Map<string, Scene> = new Map(); // 实体ID到场景的映射
  private entities: Map<string, any> = new Map(); // 存储所有实体

  /**
   * 通知实体的所有观察者
   * @param entityId 发生变化的实体ID
   * 类似于Zed GPUI中的app.notify()
   */
  notify(entityId: string): void {
    // 将实体添加到待处理通知集合
    this.pendingNotifications.add(entityId);

    // 立即处理通知
    this.flushNotifications();

    // 触发场景更新
    this.performRender(entityId);
  }

  /**
   * 观察另一个实体的变化
   * @param observedId 被观察实体的ID
   * @param observerId 观察者实体的ID
   * @param callback 观察到变化时的回调函数
   * @returns 订阅对象，可以用于取消订阅
   * 类似于Zed GPUI中的app.observe()
   */
  observe(observedId: string, observerId: string, callback: ObserverCallback): Subscription {
    // 确保被观察实体的观察者映射存在
    if (!this.observers.has(observedId)) {
      this.observers.set(observedId, new Map());
    }

    // 生成唯一的订阅ID
    const subscriptionId = `sub_${this.nextSubscriptionId++}`;
    
    // 存储观察关系
    const entityObservers = this.observers.get(observedId)!;
    entityObservers.set(subscriptionId, callback);
    
    // 存储订阅信息
    this.subscriptions.set(subscriptionId, { observedId, observerId });
    
    return new Subscription(subscriptionId, this);
  }

  /**
   * 取消观察
   * @param subscription 订阅对象
   * 类似于Zed GPUI中的app.unobserve()
   */
  unobserve(subscription: Subscription): void {
    const subscriptionId = subscription.getId();
    const subscriptionInfo = this.subscriptions.get(subscriptionId);
    
    if (subscriptionInfo) {
      const { observedId } = subscriptionInfo;
      const entityObservers = this.observers.get(observedId);
      
      if (entityObservers) {
        entityObservers.delete(subscriptionId);
        
        // 如果没有观察者了，清理映射
        if (entityObservers.size === 0) {
          this.observers.delete(observedId);
        }
      }
      
      this.subscriptions.delete(subscriptionId);
    }
  }



  /**
   * 获取待处理的通知
   */
  getPendingNotifications(): Set<string> {
    return new Set(this.pendingNotifications);
  }

  /**
   * 清除所有待处理的通知
   */
  clearPendingNotifications(): void {
    this.pendingNotifications.clear();
  }

  /**
   * 调度渲染
   * 在实际应用中，这里可能会使用requestAnimationFrame或其他机制来批量处理更新
   */
  scheduleRender(): void {
    // 简单实现，实际应用中可能需要更复杂的调度逻辑
    // 例如使用requestAnimationFrame来批量处理更新
    if (this.renderer) {
      this.scenes.forEach((scene, entityId) => {
        try {
          this.renderer.handleContextNotification(scene);
        } catch (error) {
          console.error(`[AppContext] 场景更新失败 (entity: ${entityId}):`, error);
        }
      });
    }
  }

  /**
   * 设置渲染器
   */
  setRenderer(renderer: any): void {
    this.renderer = renderer;
  }

  /**
   * 注册场景到实体ID
   */
  registerScene(entityId: string, scene: Scene): void {
    this.scenes.set(entityId, scene);
  }

  /**
   * 执行渲染操作
   */
  performRender(entityId: string): void {
    // 如果有渲染器，尝试获取关联的场景并触发更新
    if (this.renderer) {
      // 获取与实体关联的场景
      if (this.scenes.has(entityId)) {
        const scene = this.scenes.get(entityId)!;
        this.renderer.handleContextNotification(scene);
      }
      // 也检查是否有与实体相关的其他场景需要更新
      // 这是简化版，实际项目中可能需要更复杂的场景-实体映射
    }
  }

  /**
   * 处理所有待处理的通知
   */
  private flushNotifications(): void {
    if (this.pendingNotifications.size === 0) {
      return;
    }

    // 复制通知集合以避免在迭代过程中修改
    const notifications = Array.from(this.pendingNotifications);
    this.pendingNotifications.clear();

    // 处理每个通知
    notifications.forEach(entityId => {
      // 检查是否有观察者
      if (this.observers.has(entityId)) {
        const observers = this.observers.get(entityId)!;
        observers.forEach((callback, observerId) => {
          try {
            callback(entityId);
          } catch (error) {
            console.error(`Error notifying observer ${observerId} about entity ${entityId}:`, error);
          }
        });
      }
    });
  }

  /**
   * 获取实体
   * @param entityId 实体ID
   */
  getEntity(entityId: string): any {
    return this.entities.get(entityId);
  }

  /**
   * 存储实体
   * @param entityId 实体ID
   * @param entity 实体对象
   */
  setEntity(entityId: string, entity: any): void {
    this.entities.set(entityId, entity);
  }

  /**
   * 创建新实体
   * 类似于Zed GPUI中的cx.new()
   */
  new<T>(factory: (cx: Context<T>) => T, scene?: Scene): Entity<T> {
    const id = `entity_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const initialValue = factory(new Context<T>(id, this, {} as T));
    const entity = new Entity<T>(id, this, initialValue);
    
    // 存储实体
    this.setEntity(id, entity);
    
    // 关联场景
    if (scene) {
      entity.setScene(scene);
      scene.setContext(new Context<T>(id, this, initialValue));
      this.scenes.set(id, scene);
    }
    
    return entity;
  }
}

/**
 * 创建默认的AppContext实例
 */
export function createAppContext(): AppContext {
  return new AppContext();
}

/**
 * 创建新的实体上下文
 */
export function createContext<T>(entityId: string, app: AppContext, entity: T): Context<T> {
  return new Context(entityId, app, entity);
}